/******************************************************************
 * @version      : 1.0
 * @Author       : zhl
 * @Date         : 2022-03-02 09:20:16
 * @LastEditors  : zhl
 * @E-Mail       : zhll168@163.com
 * @Description  : 业务描述
 ******************************************************************/



#include <stdlib.h>
#include <stdio.h>
#include <typeinfo>
#include <sstream>
#include <float.h>

#include "HandleData.h"
#include "dlt698_global.h"
#include "CDBManager.h"
#include "hex.h"
#include "dbif.h"
#include "common.h"
#include "endian.h"
#include "CReportManager.h"

#define LINKAGE_TOPIC      "pemd/rule/linkage/pemd/v1/default"


#define  alert() { \
    if(tmp_value > point.upper_limit){ \
        handle_alert(dev_id, point.id, dstValue, point.upper_desc);  \
    } if(tmp_value < point.lower_limit){ \
        handle_alert(dev_id, point.id, dstValue, point.lower_desc);  \
    } else { \
        restore_alert(dev_id, point.id, dstValue); \
    } \
    parse_linkage(mm, point, dev_id, dstValue); \
    return dstValue; \
} 


HandleData HandleData::c_hd;

HandleData::HandleData(){
}

HandleData::~HandleData(){
}

HandleData &HandleData::getInstance(){
    return c_hd;
}
std::string HandleData::doubleToStr(const char *fmt, const double &val){
    if((NULL == fmt) || (1 > strlen(fmt))){
        return std::to_string(val);
    }
    char ret[32] = {0};
    sprintf(ret, fmt, val);
    std::string str(ret);
    return str;
}
//
template<typename T>
std::string HandleData::get_real_value(DLT698_POINT &point, T value){
    float v = 0.0f;
    char ret_buf[64] = {0};
    char str_format[32] = {0};
    if(point.formula.empty()){
        if(1.0f == point.scale){
            v = value;
        } else {
            v = value * point.scale;
        }
    } else {
        std::string formula = "echo \""+point.formula + "\"|bc";
        std::stringstream fstr;
        fstr << value;
        
        formula = string_replace(formula, "X", fstr.str());
        if(0 == exec_cmd((char *)formula.c_str(), ret_buf, sizeof(ret_buf))){
            v = atof(ret_buf);
        }
    }
    if(0 < point.decimal){
        snprintf(str_format, sizeof(str_format), "%%.%df", point.decimal);
        snprintf(ret_buf, sizeof(ret_buf), str_format, v);
    } else {
        long nscale = (long)point.scale;
        if(fabs(point.scale-nscale) < FLT_EPSILON) {
            if(std::is_same<T,float>::value || std::is_same<T, double>::value){
                snprintf(ret_buf, sizeof(ret_buf), "%lf", v);
            }else if(std::is_same<T,char>::value || std::is_same<T, short>::value || 
                std::is_same<T,int>::value || 
                std::is_same<T,long>::value || std::is_same<T, long long>::value){
                snprintf(ret_buf, sizeof(ret_buf), "%lld", (long long)v);
            } else {
                snprintf(ret_buf, sizeof(ret_buf), "%llu", (uint64_t)v);
            }
        } else {
            snprintf(ret_buf, sizeof(ret_buf), "%lf", v);
        }
    }
    
    return ret_buf;
}
/**
 * @brief 可优化
 * 
 * @param dev_id 
 * @param value 
 * @param len 
 * @param point 
 * @param data_type 
 * @return std::string 
 */
std::string HandleData::get_data_value(std::shared_ptr<CMosquittoManager> mm, int64_t dev_id, uint8_t *value, int len, DLT698_POINT point, int data_type){
    if((NULL == dlt698_global_conf) || (NULL == value)){
        return  "";
    }
    
    std::shared_ptr<DLT698_DATA_TYPE> dataType = dlt698_global_conf->get_dlt698_data_type(data_type);
    if(nullptr == dataType){
        return  "";
    }
    if(dataType->name.empty()){
        CLogger::getInstance()->LOG_W("数据类型名称为空");
        return "";
    }
    if(0 == dataType->data_type.compare("BASE")){
        std::string strLower = "";
        strLower.resize(dataType->name.size());
        transform(dataType->name.begin(), dataType->name.end(), strLower.begin(), ::tolower);
        if((0 == strLower.compare((const char *)"char")) || 
            (0 == strLower.compare((const char *)"unsigned char")) || 
            (0 == strLower.compare((const char *)"uint8_t"))){
            if(1 == dataType->size){
                return std::to_string(*(uint8_t *)value);
            }else{
                int v_len = len*2+1;
                char dst_buf[v_len] = {0};
                if(0 <= hex_to_str(value, len, dst_buf, &v_len)){
                    std::string dstValue = dst_buf;
                    return dstValue;
                }
                return "";
            }
        } else if((0 == strLower.compare((const char *)"short"))){
            std::string dstValue = get_real_value<short>(point, be16toh(*(uint16_t *)value));
            uint16_t tmp_value = (short)std::stoi(dstValue);
            alert();
        } else if((0 == strLower.compare((const char *)"unsigned short")) || 
                  (0 == strLower.compare((const char *)"uint16_t"))){
            std::string dstValue = get_real_value<uint16_t>(point, be16toh(*(uint16_t *)value));
            uint16_t tmp_value = (uint16_t)std::stoi(dstValue);
            alert();
        } else if((0 == strLower.compare((const char *)"int")) || (0 == strLower.compare((const char *)"integer"))){
            std::string dstValue = get_real_value<int>(point, be32toh(*(uint32_t *)value));
            int tmp_value = (int)std::stoi(dstValue);
            //处理报警信息
            alert();
        } else if((0 == strLower.compare((const char *)"uint32_t")) || (0 == strLower.compare((const char *)"unsigned int"))){
            std::string dstValue = get_real_value<uint32_t>(point, be32toh(*(uint32_t *)value));
            uint32_t tmp_value = (uint32_t)std::stoi(dstValue);
            //处理报警信息
            alert();
        } else if((0 == strLower.compare((const char *)"long"))){
            std::string dstValue = get_real_value<long>(point, be64toh(*(unsigned long *)value));
            long tmp_value = (long)std::stol(dstValue);
            //处理报警信息
            alert();
        } else if((0 == strLower.compare((const char *)"unsigned long"))){
            std::string dstValue = get_real_value<unsigned long>(point, be64toh(*(unsigned long *)value));
            unsigned long tmp_value = (unsigned long)std::stol(dstValue);
            //处理报警信息
            alert();
        } else if((0 == strLower.compare((const char *)"long long"))){
            std::string dstValue = get_real_value<long long>(point, be64toh(*(uint64_t *)value));
            long long tmp_value = (long long)std::stoll(dstValue);
            //处理报警信息
            alert();
        } else if((0 == strLower.compare((const char *)"unsigned long long")) || 
                  (0 == strLower.compare((const char *)"uint64_t"))){
            std::string dstValue = get_real_value<uint64_t>(point, be64toh(*(uint64_t *)value));
            uint64_t tmp_value = (uint64_t)std::stoll(dstValue);
            //处理报警信息
            alert();
        } else if((0 == strLower.compare((const char *)"float"))){
            std::string dstValue = get_real_value<float>(point, *(float *)value);
            float tmp_value = (float)std::stof(dstValue);
            alert();
        } else if((0 == strLower.compare((const char *)"double"))){
            std::string dstValue = get_real_value<double>(point, *(double *)value);
            double tmp_value = (double)std::stod(dstValue);
            alert();
            //return tmp_value.ToString(strfmt);
        } else if((0 == strLower.compare((const char *)"string"))){
            std::string dstValue = (char *)value;
            return dstValue;
        } else {
            if(1 == dataType->size){
                return std::to_string(*(uint8_t *)value);
            }else{
                int v_len = len*2+1;
                char dst_buf[v_len] = {0};
                if(0 <= hex_to_str(value, len, dst_buf, &v_len)){
                     std::string dstValue = dst_buf;
                    return dstValue;
                }
                return "";
            }
        }
    } else if(0 == dataType->data_type.compare("EQUAL")){
        get_data_value(mm, dev_id, value, len, point, dataType->equal_system_data_type);
    } else if(0 == dataType->data_type.compare("ARRAY")){
        int v_len = len*2+1;
        char dst_buf[v_len] = {0};
        if(0 <= hex_to_str(value, len, dst_buf, &v_len)){
                std::string dstValue = dst_buf;
            return dstValue;
        }
        return "";
    } else {  
        CLogger::getInstance()->LOG_W("不支持的数据类型(%s)", dataType->data_type.c_str());
        return "";
    }
    return "";
}
int HandleData::restore_alert(int64_t dev_id, int64_t point_id, std::string oadv){
    std::vector<ALERT_DATA> alerts = query_dlt698_alert(dev_id, point_id, -1);
    if(alerts.empty()){
        return -1;
    }
    ALERT_DATA alert = alerts.back();
    if(2 != alert.state){
        alert.state = 2;
        alert.restore_time = get_timestamp();
        replace_alert_data(alert);
    }
    return 0;
}
int HandleData::is_linkage(std::string op, std::string value1, std::string value2){
    
    if(0 == op.compare(">")){
        if(0 < value1.compare(value2)){
            return 1;
        }

    } else if(0 == op.compare(">=")){
        if(0 <= value1.compare(value2)){
            return 1;
        }
    } else if(0 == op.compare("<")){
        if(0 > value1.compare(value2)){
            return 1;
        }
    } else if(0 == op.compare("<=")){
        if(0 >= value1.compare(value2)){
            return 1;
        }
    } else if(0 == op.compare("!=")){
        if(0 != value1.compare(value2)){
            return 1;
        }
    }
    return 0;
}

int HandleData::parse_linkage(std::shared_ptr<CMosquittoManager> mm, DLT698_POINT &point, int64_t dev_id, std::string value){
    if(nullptr == mm){
        return -1;
    }
    auto linkage_rule_ids = dlt698_global_conf->get_linkage_point_rules(dev_id, point.id);
    if(nullptr == linkage_rule_ids){
        return -1;
    }
    for(auto linkage_rule_id = linkage_rule_ids->begin(); linkage_rule_id != linkage_rule_ids->end(); linkage_rule_id++){
        auto linkage_rule = dlt698_global_conf->get_linkage_rule(*linkage_rule_id);
        if(nullptr == linkage_rule){
            continue;
        }
        if((true == linkage_rule->is_use) && 1 == is_linkage(linkage_rule->op, value, linkage_rule->condition_value)){
            char buf[128] = {0};
            snprintf(buf, sizeof(buf), "{\"dev_id\":%lld,\"cmd_id\":%lld}", linkage_rule->linkage_dev_id, linkage_rule->command_id);
            mm->publish(LINKAGE_TOPIC, strlen(buf), (void *)buf);
        }
    }
    return 0;
}
int HandleData::handle_alert(int64_t dev_id, int64_t point_id, std::string oadv, std::string alert_desc){
    std::vector<ALERT_DATA> alerts = query_dlt698_alert(dev_id, point_id, 0);
    
    auto point = dlt698_global_conf->get_dlt698_point(point_id);
    if(nullptr == point){
        return -1;
    }
    if(alerts.empty()){
        ALERT_DATA alert;
        alert.alert_count = 1;
        alert.dev_id = dev_id;
        alert.point_id = point_id;
        alert.value = oadv;
        alert.state = 0;
        alert.report_time = get_timestamp();
        alert.restore_time = 0;
        alert.alert_desc = alert_desc;
        alert.id = insert_alert_data(alert);
        CReportManager::getInstance().report_alert(alert);
    } else {
        //threshold_count
        ALERT_DATA alert = alerts.front();
        alert.alert_desc = alert_desc;
        if(0 == alert.state){
            alert.alert_count++;
            alert.report_time = get_timestamp();
            if(alert.alert_count > point->threshold_count){
                alert.state = 1;
                //上报数据到云端
                CReportManager::getInstance().report_alert(alert);
            }
            replace_alert_data(alert);
        } else {//理论上不会走这里
            alert.alert_count = 1;
            alert.state = 0;
            alert.value = oadv;
            alert.report_time = get_timestamp();
            alert.restore_time = 0;
            alert.id = insert_alert_data(alert);
            CReportManager::getInstance().report_alert(alert);
        }
    }

    return 0;
}
//可优化
int HandleData::handle(std::shared_ptr<CMosquittoManager> mm, Data &data, TASK_PARAMS *params){
    if(NULL == dlt698_global_conf){
        return -1;
    }
    //
    std::shared_ptr<TERMINAL_DEVICE> v_dev = dlt698_global_conf->get_terminal_device(data.c_dev_id);
    if(nullptr == v_dev){
        return -1;
    }
    //
    int len = sizeof(OAD_TYPE)*2+1;
    char oad_buf[len] = {0};
    hex_to_str(data.c_oad, sizeof(OAD_TYPE), oad_buf, &len);
    std::string oad = oad_buf;
    //通过查数据库
    vector<DLT698_POINT> v_points = CDBManager::getInstance()->query_dlt698_point(v_dev->protocol_id, oad);
    if(v_points.empty()){
        return -1;
    }
    //
    int i = 0;
    //    
    std::shared_ptr<DLT698_DATA_TYPE> dataType = dlt698_global_conf->get_dlt698_data_type(v_points[0].data_type);
    if(nullptr == dataType){
        return  -1;
    }
    std::unordered_map<std::string, RESPONSE_DATA> points;
    if((0 == dataType->data_type.compare("BASE")) || (0 == dataType->data_type.compare("EQUAL"))){
        for(auto content = data.c_contents.begin(); content != data.c_contents.end(); content++){
            if(NULL != content->second){
                if(i < (int)v_points.size()){
                    RESPONSE_DATA rdata;
                    rdata.code = 0;
                    rdata.msg = "successed";
                    rdata.name = v_points[i].name;
                    rdata.point_id = v_points[i].id;
                    rdata.data_type = TASK_TYPE_COLLECT; //改动地方太多，固定处理
                    rdata.value =  get_data_value(mm, data.c_dev_id, content->second, content->first, v_points[i], v_points[i].data_type);
                    points.insert(std::make_pair(rdata.name, rdata));
                    i++;
                } else {
                    break;
                }
            }
        }
    }else{
        std::string dstValue = "";
        for(auto content = data.c_contents.begin(); content != data.c_contents.end(); content++){
            int v_len = content->first*2+1;
            char dst_buf[v_len] = {0};
            if(0 == hex_to_str(content->second, content->first, dst_buf, &v_len)){
                dstValue += dst_buf;
            }
        }
        RESPONSE_DATA rdata;
        
        rdata.point_id = v_points[i].id;
        rdata.data_type = TASK_TYPE_COLLECT; 
        rdata.value =  dstValue;
        rdata.name = v_points[i].name;
        rdata.msg = "successed";
        rdata.code = 0;
        points.insert(std::make_pair(rdata.name, rdata));
    }
    if(0 < points.size()){
        if(params->type == TASK_TYPE_GET_CMD_RULE){
            CReportManager::getInstance().report_get_cmd_data(params, "CMD_GET_COMMAND_RULE", mm, points);
        } else {
            CReportManager::getInstance().push_data(v_dev->id, points);
        }
        
    }
    return 0;
}